package ar.edu.unlp.info.tesis.database.service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.hibernate.HibernateException;
import org.springframework.http.HttpStatus;

import ar.edu.unlp.info.tesis.controladores.lrs.exceptions.StatementException;
import ar.edu.unlp.info.tesis.database.exception.DatabaseException;
import ar.edu.unlp.info.tesis.domain.Actor;
import ar.edu.unlp.info.tesis.domain.Agent;
import ar.edu.unlp.info.tesis.domain.Context;
import ar.edu.unlp.info.tesis.domain.Group;
import ar.edu.unlp.info.tesis.domain.Statement;
import ar.edu.unlp.info.tesis.domain.Verb;

public class StatementService extends GenericService {

	public StatementService()
	{
		this.setClazz(Statement.class);
	}


	public void create(Statement statement)
	{
		if(statement.getContext() != null)
			this.save(statement.getContext());
		this.save(statement);
	}

	/**
	 * Se crea un statement y se lo asocia al contexto mediante las extensiones.
	 * 
	 * @param statement
	 */
	public void create(long idStatement, Statement extensionStatement)
	{
		this.create(extensionStatement);
		new ContextService().addExtension(idStatement, extensionStatement);
	}

	/**
	 * Se crea un statement, junto a su contexto y object
	 * 
	 * @param statement
	 * @param teamId
	 */
	public void create(Statement statement, long teamId)
	{
		Group team = (Group)new ActorService().getById(teamId);
		Context context=new Context();
		context.setTeam(team);

		super.save(context);

		statement.setContext(context);	
		this.save(statement.getObject());
		this.save(statement);
	}

	/**
	 * Recupera un statement dado junto a todas sus relaciones.
	 * 
	 * @param id
	 * @return
	 */
	public Statement get(long id, long idAgent)
	{
		Statement statement = (Statement)this.getById(id);
		createHistory(idAgent, statement);
		return statement;
	}

	/**
	 * Obtiene todos los statemens para un actor
	 * 
	 * @param idAgent
	 * @return
	 */
	public List<Statement> getAll(long idAgent)
	{
		Actor actor = (Actor) new ActorService().getById(idAgent);
		this.addEqualRestriction("actor", actor, false);
		return this.getList();
	}


	/**
	 * 
	 * Genera la lista de statements perteneciente a un gupo dado.
	 * 
	 * Para esto considerar
	 *  Statement -> context -> Group
	 * 
	 * 
	 * @param idActor
	 * @param idGroup
	 * @return statements
	 */
	public List<Statement> getAllXGroup(long idGroup) {

		List<Statement> statements=new ArrayList<Statement>();

		Group team = (Group) new ActorService().getById(idGroup);
		this.addEqualRestriction("context.team", team, false);
		statements =this.getList();
		return statements;
	}



	/**
	 * Crea un statement historial
	 * 
	 * @param idAgent
	 * @param statement
	 */
	private void createHistory(long idAgent, Statement statement)
	{
		Statement historial = new Statement();
		historial.setActor((Actor)new ActorService().getById(idAgent));
		historial.setVerb((Verb)new VerbService().getById(3));//3= visto
		historial.setObject(statement);

		this.create(historial);
	}


	public void verificarStatement(String statementId, String newStatement) throws StatementException, Exception
	{
		Statement oldStatement = (Statement) this.getById(statementId);

		if(oldStatement != null && !oldStatement.getOriginalJSON().equals(newStatement))
			throw new StatementException("An statement identified by " + statementId + " already exist", HttpStatus.CONFLICT);
	}


	public List<Statement> getStatements(String voidedStatementId, Actor agent, String verb, String activity, String registration, boolean related_activities, boolean related_agents, Date since, Date until, Integer limit, boolean ascending, String parentId) throws HibernateException, DatabaseException
	{
		this.addOrderParam("stored", ascending);
		
		if(agent != null)
		{
			if(agent.getId() != null)
				agent = (Actor)GenericService.getService(Actor.class).getByProperty("id", agent.getId());
			else if(agent.getName() != null)
				agent = (Actor)GenericService.getService(Actor.class).addLikeRestriction("name", agent.getName(), "ANYWHERE", false).uniqueResult();
			
			this.createDisjuntion();
			
			try
			{
				Group group = (Group)agent;
				for(Agent a: group.getMembers())
					this.addEqualRestriction("actor", a, true);
				this.addEqualRestriction("object", group, true);
			}
			catch(Exception e){
				
				this.addEqualRestriction("actor", agent, true);
				this.addEqualRestriction("object", agent, true);
			}
			
			if(related_agents)
			{
				
			}
		}
		
		if(verb != null)
		{
			this.addAlias("verb", "verb");
			this.addEqualRestriction("verb.id", verb, false);
		}
		
		if(activity != null)
		{
			this.addAlias("activity", "activity");
			this.addEqualRestriction("activity.id", activity, false);
			
			if(related_activities)
			{
				
			}
		}
		
		if(parentId != null)
		{
			this.addAlias("context");
			this.addAlias("context.statement");
			this.addEqualRestriction("statement.id", parentId, false);
		}
		
		if(since != null)
			this.addGreaterEqualRestriction("stored", since, false);
		
		if(until != null)
			this.addLeastEqualRestriction("stored", since, false);
			
		if(limit != 0)
			return this.next(0, limit);
		else
			return this.getList();
	}


	public Statement toStatement(String jsonStatement) throws JsonParseException, JsonMappingException, IOException
	{
		ObjectMapper mapper = new ObjectMapper();
		Statement statement = mapper.readValue(jsonStatement, Statement.class);
		return statement;
	}
	
	public String toJSON(Statement statement) throws JsonGenerationException, JsonMappingException, IOException
	{
		ObjectMapper mapper = new ObjectMapper();
		String json = mapper.writeValueAsString(statement);
		return json;
	}
}
